package com.my.handler;

import com.alibaba.fastjson2.JSON;
import com.my.holder.ContextHolder;
import com.my.resquest.NettyHttpRequest;
import com.my.utils.AnnotationUtils;
import com.my.utils.PrintUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;

import java.util.Arrays;

/**
 * HTTP 请求处理器
 */
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        ContextHolder.removeContext();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
        String uri = req.uri();
        HttpMethod method = req.method();

        if (uri.contains("?")) {
            uri = uri.split("\\?")[0];
        }
        if (AnnotationUtils.check404(uri)) {
            PrintUtil.warn("404 " + method + " " + uri);
            FullHttpResponse response = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1,
                    HttpResponseStatus.NOT_FOUND,
                    Unpooled.copiedBuffer(PrintUtil.printHTML("404.html"), CharsetUtil.UTF_8));
            // 设置头信息
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_HTML + "; charset=UTF-8");
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
            return;
        }

        if (AnnotationUtils.check405(uri, method.name())) {
            PrintUtil.warn("405 " + method + " " + uri);
            FullHttpResponse response = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1,
                    HttpResponseStatus.METHOD_NOT_ALLOWED,
                    Unpooled.copiedBuffer(PrintUtil.printHTML("405.html"), CharsetUtil.UTF_8));
            // 设置头信息
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_HTML + "; charset=UTF-8");
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
            return;
        }
        ContextHolder.setContext(ctx);
        NettyHttpRequest nettyHttpRequest = transformRequest(req);
        //request
        Object[] args = {nettyHttpRequest};
        PrintUtil.info(method + " " + uri);
        //支持前置中间件
        AnnotationUtils.execBefore(nettyHttpRequest);

        ctx.channel().attr(AttributeKey.valueOf(Boolean.class, "isContinue")).set(Boolean.TRUE);
        //exec
        Object result = AnnotationUtils.executeRequest(uri, method.name(), args);
        //特殊请求判断
        Boolean flag = (Boolean) ctx.channel().attr(AttributeKey.valueOf(Boolean.class, "isContinue")).get();
        if (!flag) {
            return;
        }

        if (result != null) {
            //返回值处理器
            result = AnnotationUtils.execResponseDevice(result);
            // 创建http响应
            FullHttpResponse response = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1,
                    HttpResponseStatus.OK,
                    Unpooled.copiedBuffer(JSON.toJSONString(result), CharsetUtil.UTF_8));
            // 设置头信息
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON + "; charset=UTF-8");
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
            ContextHolder.removeContext();
        }

        FullHttpResponse response = new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1,
                HttpResponseStatus.OK);
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN + "; charset=UTF-8");
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        ContextHolder.removeContext();
    }


    /**
     * 包装转换 request请求
     *
     * @param req
     * @return
     */
    private NettyHttpRequest transformRequest(FullHttpRequest req) {
        NettyHttpRequest nettyHttpRequest = new NettyHttpRequest();
        nettyHttpRequest.setRequest(req);
        return nettyHttpRequest;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 打印异常
        StackTraceElement stackTraceElement = Arrays.stream(cause.getCause().getStackTrace()).findFirst().get();
        PrintUtil.error(cause.getMessage() + " " + stackTraceElement);
//        cause.getCause().printStackTrace(System.err);
        Object exceptionResult = AnnotationUtils.execExceptionHandler(cause.getCause().getClass());
        if (exceptionResult != null) {
            FullHttpResponse response = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR,
                    Unpooled.copiedBuffer(JSON.toJSONString(exceptionResult), CharsetUtil.UTF_8)
            );
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_HTML + "; charset=UTF-8");
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());

            // 写入错误响应并关闭连接
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }
        //HTML return
        FullHttpResponse response = new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR,
                Unpooled.copiedBuffer(PrintUtil.printHTML("500.html").replace("{msg}", cause.getMessage()), CharsetUtil.UTF_8)
        );
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_HTML + "; charset=UTF-8");
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());

        // 写入错误响应并关闭连接
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

}